package com.jiuqi.abc.model.service;

import com.jiuqi.abc.ABCException;
import com.jiuqi.abc.intf.ABCIntfException;
import com.jiuqi.abc.intf.provider.dimension.IDimProvider;
import com.jiuqi.abc.intf.provider.member.IMemberProvider;
import com.jiuqi.abc.intf.provider.member.Member;
import com.jiuqi.abc.model.bean.*;
import com.jiuqi.abc.model.dao.ABCDriverDao;
import com.jiuqi.abc.model.vo.*;
import com.jiuqi.bi.util.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.xssf.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author 龚泽楷
 * @date 2023年04月23日 13:54
 */
@Service
public class ABCDriverService {
    @Autowired
    private ABCModelService abcModelService;

    @Autowired
    private ABCBeanService abcBeanService;

    @Autowired
    private ABCDriverGroupService abcDriverGroupService;

    //自身注入自身，解决调用本类方法导致传播行为失效（Transactional）
    @Autowired
    private ABCDriverService driverService;

    @Autowired
    private ABCDriverDao abcDriverDao;

    @Autowired
    private ABCDriverItemService abcDriverItemService;

    @Autowired
    private ABCDriverItemFormulaService abcDriverItemFormulaService;

    @Autowired
    IDimProvider dimProvider;

    @Autowired
    IMemberProvider memberProvider;


    private static final Logger logger = LoggerFactory.getLogger(ABCDriverService.class);

    private static final String DRIVER_ERROR_NULL = "异常:动因不应为空";

    /**
     * 批量添加动因
     *
     * @param drivers 动因集合
     */
    @Transactional(rollbackFor = Exception.class)
    public void batchAddDriversAndDriverItems(List<Driver> drivers) {

        String modelId = "";
        if (!drivers.isEmpty()) {
            modelId = drivers.get(0).getBelongModelId();
        }

        //添加动因
        abcDriverDao.batchAddDrivers(drivers);

        //添加动因项
        for (Driver driver : drivers) {
            List<DriverItem> driverItemList = driver.getDriverItemList();
            for (DriverItem item : driverItemList) {
                item.setBelongDriverId(driver.getId());
                item.setBelongDriverGroupId(driver.getBelongDriverGroupId());
                item.setBelongModelId(driver.getBelongModelId());
            }
            abcDriverItemService.batchAddDriverItem(driverItemList);

            //添加动因项取数公式
            List<DriverItemFormula> driverItemFormulaList = new ArrayList<>();
            for (DriverItem item : driverItemList) {
                DriverItemFormula driverItemFormula = item.getDriverItemFormula();
                driverItemFormula.setBelongDriverItemId(item.getId());
                driverItemFormulaList.add(driverItemFormula);
            }
            abcDriverItemFormulaService.batchAddDriverItemFormula(driverItemFormulaList);
        }

        //更新abc模型的修改时间
        if (!"".equals(modelId)) {
            abcBeanService.updateModifyTime(modelId);
        }

    }

    /**
     * 批量保存动因
     *
     * @param driverGroupId 动因分组ID
     * @param drivers       待保存动因（内含动因项、动因项公式）
     */
    @Transactional(rollbackFor = Exception.class)
    public void batchSaveDriversAndDriverItems(String driverGroupId, List<Driver> drivers) {
        //获取当前模型ID
        String modelId = abcDriverGroupService.getDriverGroupByDriverGroupId(driverGroupId).getBelongModelId();

        //删除 当前动因分组下所有动因
        List<String> driverGroupIdList = new ArrayList<>();
        driverGroupIdList.add(driverGroupId);
        driverService.batchDeleteDriversByDriverGroupIdList(driverGroupIdList);

        //添加 页面中所有动因
        driverService.batchAddDriversAndDriverItems(drivers);

        //更新abc模型的修改时间
        abcBeanService.updateModifyTime(modelId);
    }


    /**
     * 根据 动因请求参数对象 批量获取 动因集合
     *
     * @param driverGroupId 动因分组ID
     * @param modelId       模型ID
     * @return 动因集合 【动因 中 有 动因项，动因项中  有 维度名称、维度项名称】
     */
    public List<Driver> getDriverListByDriverGroupId(String driverGroupId, String modelId) {
        //获取时期参数
        ABCHomeVo abcHomeVo = abcModelService.queryPeriodAndDW(modelId);
        List<String> periodBeanList = abcHomeVo.getPeriodBeanList();
        String period = periodBeanList.get(periodBeanList.size() - 1);

        //根据 动因分组ID 获取 动因集合
        List<String> driverGroupIdList = new ArrayList<>();
        driverGroupIdList.add(driverGroupId);
        List<Driver> driverList = getDriverListByDriverGroupIds(driverGroupIdList);

        //根据 动因分组ID 获取 动因项集合
        List<DriverItem> driverItemList = abcDriverItemService.getDriverItemListByDriverGroupIdAndPeriod(driverGroupId, period);

        return assembleDriverItemForDriver(driverItemList, driverList);
    }

    /**
     * 根据 动因分组ID集合 批量获取 动因集合
     *
     * @param driverGroupIdList 动因分组表主键集合
     * @return 动因集合 【动因 中 没有 动因项】
     */
    public List<Driver> getDriverListByDriverGroupIds(List<String> driverGroupIdList) {
        return abcDriverDao.getDriverListByDriverGroupIds(driverGroupIdList);
    }

    /**
     * [非本模块使用]
     * 根据 模型ID 获取 动因集合
     *
     * @param modelId 模型ID
     * @return 动因集合 【动因 中 有 动因项 | 动因项 中 有 动因项公式,没有 维度名称、维度项名称】
     */
    public List<Driver> getDriverListByModelId(String modelId) {
        //根据 模型ID 获取 动因集合
        List<Driver> driverList = abcDriverDao.getDriverListByModelId(modelId);

        //根据 模型ID 获取 动因项集合
        List<DriverItem> driverItemList = abcDriverItemService.getDriverItemListByModelId(modelId);

        return assembleDriverItemForDriver(driverItemList, driverList);
    }

    /**
     * 为 动因集合 装配 动因项集合
     *
     * @param driverItemList 动因项集合
     * @param driverList     动因集合
     * @return 装配后动因集合
     */
    private List<Driver> assembleDriverItemForDriver(List<DriverItem> driverItemList, List<Driver> driverList) {
        //获取动因项集合中元素个数大于0
        if (!driverItemList.isEmpty()) {
            //先分组形成map,后将map流化，给匹配的driver添加动因项集合
            driverItemList.stream()
                    .collect(Collectors.groupingBy(DriverItem::getBelongDriverId))
                    .forEach((key, value) -> {
                        //获取动因集合中元素个数大于0
                        if (!driverList.isEmpty()) {
                            driverList.stream()
                                    .filter(driver -> key.equals(driver.getId()))
                                    .forEach(selectedDriver -> {
                                        selectedDriver.setDriverItemList(value);

                                        //设置维度标题集合
                                        List<String> dimTitleList = new ArrayList<>();
                                        value.stream()
                                                .map(DriverItem::getDimTitle)
                                                .distinct()
                                                .forEach(dimTitleList::add);
                                        selectedDriver.setDimTitleList(dimTitleList);
                                    });
                        }
                    });
        }
        return driverList;
    }


    /**
     * 处理 导入文件
     *
     * @param file 导入文件
     */
    @Transactional(rollbackFor = Exception.class)
    public void processImportData(String modelId, String currentDriverGroupId, MultipartFile file) throws ABCException {

        //【获取excel数据，组装driver对象集合】
        if (file.isEmpty()) {
            String exceptionInfo = "导入失败，导入文件为空";
            logger.error(exceptionInfo);
            throw new ABCException(exceptionInfo);
        }
        //由输入流获取excel的实例
        XSSFWorkbook wb = null;
        try {
            wb = new XSSFWorkbook(file.getInputStream());
        } catch (IOException e) {
            String exceptionInfo = "导入失败，输入流处理异常";
            logger.error(exceptionInfo);
            throw new ABCException(exceptionInfo);
        }

        //获取第一个页签
        XSSFSheet sheet = wb.getSheetAt(0);

        //记录 全由 null、“” 构成的空行
        List<Integer> emptyRows = new ArrayList<>();
        processEmptyRows(sheet,emptyRows);

        //动因集合
        List<Driver> driverList = new ArrayList<>();
        //动因
        Driver driver = null;
        //动因项集合
        List<DriverItem> driverItemList = new ArrayList<>();

        //审核表头
        checkSheetHeader(sheet.getRow(0));

        //循环sesheet页中数据从第二行开始，第一行是标题
        for (int i = 1; i < sheet.getPhysicalNumberOfRows(); i++) {
            //全由 null、“” 构成的空行 跳过
            if (emptyRows.contains(i))
                continue;

            //获取每一行数据,先获取一个行，之后获取这一个行中的所有列
            XSSFRow row = sheet.getRow(i);

            //每遍历一行都需要 创建一个 动因项对象、动因项公式对象
            DriverItem driverItem = new DriverItem();
            DriverItemFormula driverItemFormula = new DriverItemFormula();

            //当前行 维度编码
            String curDimName = "";
            for (int idx = 0; idx < row.getLastCellNum(); idx++) {
                Cell cell = row.getCell(idx);

                DataFormatter formatter = new DataFormatter();
                String cellValue = formatter.formatCellValue(cell).trim();

                if (judgePass(idx, cell, cellValue))
                    continue;

                DriverImportParams driverImportParams = new DriverImportParams(sheet,row,i,idx,modelId,currentDriverGroupId);
                processCurRowNoneNineCol(driverImportParams,driverList, driver, driverItemList,driverItem,driverItemFormula);

                switch (idx) {
                    //遍历到一行中的第一列时,如果对应单元格值 不为 空字符串，则创建动因对象,创建动因项集合
                    case 0:
                        driverItemList = new ArrayList<>();
                        //初始化本组的动因和动因项集合
                        driver = new Driver();
                        auditCellZero(cellValue, driverList);
                        driver.setCode(cellValue.toUpperCase());
                        break;
                    case 1:
                        auditCellOne(cellValue);
                        assert driver != null : "异常：动因不应为空";
                        driver.setName(cellValue);
                        break;
                    case 2:
                        auditCellTwo(modelId, cellValue);
                        driverItem.setDimName(cellValue);
                        curDimName = cellValue;
                        break;
                    case 3:
                        auditCellThree(cellValue);
                        driverItem.setDimTitle(cellValue);
                        break;
                    case 4:
                        auditCellFour(modelId, curDimName, driver, driverItemList, driverItem, cellValue);
                        driverItem.setMemberCode(cellValue);
                        break;
                    case 5:
                        auditCellFive(cellValue);
                        driverItem.setDimItemName(cellValue);
                        break;
                    case 6:
                        auditCellSix(cellValue);
                        driverItem.setCode(cellValue);
                        break;
                    case 7:
                        auditCellSeven(cellValue);
                        driverItem.setName(cellValue);
                        break;
                    case 8:
                        driverItemFormula.setBelongModelId(modelId);
                        driverItemFormula.setFormula(cellValue);

                        driverItem.setDriverItemFormula(driverItemFormula);
                        driverItem.setBelongModelId(modelId);
                        driverItem.setBelongDriverGroupId(currentDriverGroupId);
                        driverItemList.add(driverItem);

                        judgeAssembleDriverTime(modelId, currentDriverGroupId, sheet, driverList, driver, driverItemList, i);
                        break;
                    default:
                        logger.error("异常值");
                }
            }
        }

        //【检测 当前模型 除当前动因分组外的 动因集合中 是否存在和待检查动因集合 相同的动因编码 存在】
        DriverCheckVo driverCheckVo = driverCodeCheckViaExistedDriversExcCurrGroup(modelId, currentDriverGroupId, driverList);
        finalProcess(currentDriverGroupId, driverList, driverCheckVo);
    }


    /**
     * 处理当前行没有九列的情况
     * @param driverImportParams    动因导入所需参数对象
     * @param driverList            动因集合
     * @param driver                动因
     * @param driverItemList        动因值集合
     * @param driverItem            动因值
     * @param driverItemFormula     动因值取数公式
     * @throws ABCException         异常
     */
    private void processCurRowNoneNineCol(DriverImportParams driverImportParams, List<Driver> driverList,
                                          Driver driver, List<DriverItem> driverItemList, DriverItem driverItem,
                                          DriverItemFormula driverItemFormula) throws ABCException {

        XSSFSheet sheet = driverImportParams.getSheet();
        XSSFRow row = driverImportParams.getRow();
        Integer rowIndex = driverImportParams.getRowIndex();
        Integer columnIndex = driverImportParams.getColumnIndex();
        String modelId = driverImportParams.getModelId();
        String currentDriverGroupId = driverImportParams.getCurrentDriverGroupId();

        if (columnIndex == row.getLastCellNum() - 1 && columnIndex != 8) {
            driverItemFormula.setBelongModelId(modelId);
            driverItemFormula.setFormula("");

            driverItem.setDriverItemFormula(driverItemFormula);
            driverItem.setBelongModelId(modelId);
            driverItem.setBelongDriverGroupId(currentDriverGroupId);
            driverItemList.add(driverItem);

            judgeAssembleDriverTime(modelId, currentDriverGroupId, sheet, driverList, driver, driverItemList, rowIndex);
        }
    }

    /**
     * 审核表头
     * @param row 行对象
     * @throws ABCException 检查异常信息
     */
    private void checkSheetHeader(XSSFRow row) throws ABCException {

        if (row == null) {
            String exceptionInfo = "表头行不应为空";
            logger.error(exceptionInfo);
            throw new ABCException(exceptionInfo);
        }

        for (int i = 0; i < row.getLastCellNum(); i++) {
            Cell cell = row.getCell(i);
            DataFormatter formatter = new DataFormatter();
            String cellValue = formatter.formatCellValue(cell).trim();
            if (i < 9 && (StringUtils.isEmpty(cellValue) || !cellText[i].equals(cellValue)) ) {
                String exceptionInfo = "错误的表头名称";
                logger.error(exceptionInfo);
                throw new ABCException(exceptionInfo);
            }
        }

    }

    /**
     * 判断 当前单元格 是否应该处理
     * @param columnIndex 单元格所属列
     * @param cell        单元格
     * @param cellValue   单元格值
     * @return 是否跳过处理
     */
    private boolean judgePass(int columnIndex,Cell cell,String cellValue){
        //导入文件中 一行内 某个单元格未经编辑 此时应该跳过未经编辑单元格（动因编码、动因名称为 null）
        //动因值编码、动因值名称、取数公式 对应单元格 可以未经编辑
        if (cell == null && !(columnIndex == 6 || columnIndex == 7 || columnIndex == 8) ) {
            return true;

        //如果动因编码、动因名称为空字符串，此时应该跳过（动因编码、动因名称为 “”）
        }else{
            return (columnIndex == 0 || columnIndex == 1) && "".equals(cellValue);
        }
    }

    /**
     * 检查当前行中第 0 个单元格
     * @param cellValue 单元格信息
     * @param driverList 动因集合
     * @throws ABCException 检查异常信息
     */
    private void auditCellZero(String cellValue, List<Driver> driverList) throws ABCException {
        //审核 动因编码 ①特殊字符 ②字符类型 ③字符长度
        auditCellCharacter(cellValue, true);

        //审核 动因编码 文件中是否重复
        if (!driverList.isEmpty()) {
            boolean isContains = driverList.stream()
                    .map(Driver::getCode)
                    .collect(Collectors.toList())
                    .contains(cellValue);

            if (isContains) {
                String checkInfo = String.format("导入失败，导入文件中动因编码（%S）重复", cellValue);
                logger.error(checkInfo);
                throw new ABCException(checkInfo);
            }
        }
    }

    /**
     * 检查当前行中第 1 个单元格
     * @param cellValue 单元格信息
     * @throws ABCException 检查异常信息
     */
    private void auditCellOne(String cellValue) throws ABCException {
        //审核 动因名称 字符长度
        if (cellValue.length() > 50) {
            String exceptionInfo = String.format("导入失败，动因名称（%S）长度大于50", cellValue);
            logger.error(exceptionInfo);
            throw new ABCException(exceptionInfo);
        }
    }

    /**
     * 检查当前行中第 2 个单元格
     * @param modelId 模型ID
     * @param cellValue 单元格信息
     * @throws ABCException 检查异常信息
     */
    private void auditCellTwo(String modelId, String cellValue) throws ABCException {
        //审核 维度编码 是否为空字符串
        if ("".equals(cellValue)) {
            String exceptionInfo = "维度编码不应为空";
            logger.error(exceptionInfo);
            throw new ABCException(exceptionInfo);
        }

        //审核 维度编码 是否存在
        try {
            dimProvider.getBean(cellValue);
        } catch (ABCIntfException e) {
            String exceptionInfo = "ABC接口异常";
            logger.error(exceptionInfo, e);
            throw new ABCException(exceptionInfo);
        } catch (NullPointerException e) {
            String exceptionInfo = String.format("导入失败，导入文件中有不存在的维度编码（%S）", cellValue);
            logger.error(exceptionInfo, e);
            throw new ABCException(exceptionInfo);
        }

        //审核 维度编码 是否属于当前模型
        List<DimBeanVo> dimBeanVos = abcModelService.queryModelDimListByModelId(modelId);
        boolean contains = dimBeanVos.stream()
                .map(DimBeanVo::getName)
                .collect(Collectors.toList())
                .contains(cellValue);

        if (!contains) {
            String exceptionInfo = String.format("导入失败，导入文件中有不属于当前模型的维度编码（%s）", cellValue);
            logger.error(exceptionInfo);
            throw new ABCException(exceptionInfo);
        }
    }

    /**
     * 检查当前行中第 3 个单元格
     * @param cellValue 单元格信息
     * @throws ABCException 检查异常信息
     */
    private void auditCellThree(String cellValue) throws ABCException {
        //审核 维度名称 是否为空字符串
        if ("".equals(cellValue)) {
            String exceptionInfo = "维度名称不应为空";
            logger.error(exceptionInfo);
            throw new ABCException(exceptionInfo);
        }
    }

    /**
     * 检查当前行中第 4 个单元格
     * @param modelId  模型ID
     * @param curDimName 当前维度编码
     * @param driver 动因
     * @param driverItemList 动因值集合
     * @param driverItem 动因值
     * @param cellValue 单元格信息
     * @throws ABCException 检查异常信息
     */
    private void auditCellFour(String modelId, String curDimName, Driver driver, List<DriverItem> driverItemList, DriverItem driverItem, String cellValue) throws ABCException {
        //审核 维成员编码 是否为空字符串
        if ("".equals(cellValue)) {
            String exceptionInfo = "维成员编码不应为空";
            logger.error(exceptionInfo);
            throw new ABCException(exceptionInfo);
        }

        //审核 维成员编码 是否存在
        try {
            ABCHomeVo abcHomeVo = abcModelService.queryPeriodAndDW(modelId);
            List<String> periodBeanList = abcHomeVo.getPeriodBeanList();
            String period = periodBeanList.get(periodBeanList.size() - 1);
            Member member = memberProvider.get(curDimName, cellValue, period);
            if (member == null) {
                String exceptionInfo = String.format("导入失败，导入文件中有不存在的维成员编码（%s）", cellValue);
                logger.error(exceptionInfo);
                throw new ABCException(exceptionInfo);
            }
        } catch (ABCIntfException e) {
            String message = "导入失败，维成员提供器查询异常";
            logger.error(message, e);
            throw new ABCException(message);
        }

        //审核 维成员编码 在同一维度中是否唯一
        if (!driverItemList.isEmpty()) {
            boolean isRepeat = false;
            for (DriverItem item : driverItemList) {
                if (item.getMemberCode().equals(cellValue)) {
                    isRepeat = item.getDimName().equals(driverItem.getDimName());
                }
            }
            if (isRepeat) {
                assert driver != null;
                String exceptionInfo = String.format("导入失败，导入文件中动因（%S）多次关联维度（%s）下维成员（%s）", driver.getName(), driverItem.getDimName(), cellValue);
                logger.error(exceptionInfo);
                throw new ABCException(exceptionInfo);
            }
        }
    }

    /**
     * 检查当前行中第 5 个单元格
     * @param cellValue 单元格信息
     * @throws ABCException 检查异常信息
     */
    private void auditCellFive(String cellValue) throws ABCException {
        //审核 维成员名称 是否为空字符串
        if ("".equals(cellValue)) {
            String exceptionInfo = "维成员名称不应为空";
            logger.error(exceptionInfo);
            throw new ABCException(exceptionInfo);
        }
    }

    /**
     * 检查当前行中第 6 个单元格
     * @param cellValue 单元格信息
     * @throws ABCException 检查异常信息
     */
    private void auditCellSix(String cellValue) throws ABCException {
        //动因值编码 不为空字符串
        if (!"".equals(cellValue)) {
            //审核 动因值编码 ①特殊字符 ②字符类型 ③字符长度
            auditCellCharacter(cellValue, false);
        }
    }

    /**
     * 检查当前行中第 7 个单元格
     * @param cellValue 单元格信息
     * @throws ABCException 检查异常信息
     */
    private void auditCellSeven(String cellValue) throws ABCException {

        //审核 动因值名称 字符长度
        if (cellValue.length() > 50) {
            String message = String.format("导入失败，动因值名称（%S）长度大于50", cellValue);
            logger.error(message);
            throw new ABCException(message);
        }
    }

    /**
     * 审核 动因编码 和 动因值编码  ①特殊字符 ②字符类型 ③字符长度
     *
     * @param cellValue    动因编码 或 动因值编码
     * @param isDriverCode 是否为动因编码
     * @return 审核信息
     */
    private void auditCellCharacter(String cellValue, boolean isDriverCode) throws ABCException {
        String checkInfo;
        if (isContainSpecial(cellValue)) {

            checkInfo = isDriverCode ? String.format("导入失败，动因编码（%S）不能包含\\ 、/ 、: 、* 、? 、\" 、' 、< 、> 、|中任意字符", cellValue)
                    :
                    String.format("导入失败，动因值编码（%S）不能包含\\ 、/ 、: 、* 、? 、\" 、' 、< 、> 、|中任意字符", cellValue);
            logger.error(checkInfo);
            throw new ABCException(checkInfo);

        } else if (checkBegin(cellValue)) {

            checkInfo = isDriverCode ? String.format("导入失败，动因编码（%S）只能由字母、数字和_组成,并且以字母开头", cellValue)
                    :
                    String.format("导入失败，动因值编码（%S）只能由字母、数字和_组成,并且以字母开头", cellValue);
            logger.error(checkInfo);
            throw new ABCException(checkInfo);

        } else if (cellValue.length() > 50) {

            checkInfo = isDriverCode ? String.format("导入失败，动因编码（%S）长度大于50", cellValue)
                    :
                    String.format("导入失败，动因值编码（%S）长度大于50", cellValue);
            logger.error(checkInfo);
            throw new ABCException(checkInfo);

        }
    }

    /**
     * 审核字符串是否包含特殊字符
     *
     * @param str 待校验字符串
     * @return true 包含  false 不包含
     */
    private boolean isContainSpecial(String str) {
        Pattern p = Pattern.compile("[/\\\\*<>\"'\\\\|?:]");
        Matcher m = p.matcher(str);
        return m.find();
    }

    /**
     * 审核字符串开头
     *
     * @param str 待校验字符串
     * @return true 通过  false 不通过
     */
    private boolean checkBegin(String str) {
        Pattern p = Pattern.compile("^[a-zA-Z]\\w{0,49}$");
        Matcher m = p.matcher(str);
        return !m.matches();
    }

    /**
     * 判断动因组装时机
     *
     * @param modelId              模型ID
     * @param currentDriverGroupId 当前动因分组ID
     * @param sheet                页签
     * @param driverList           动因集合
     * @param driver               待组装动因
     * @param driverItemList       动因值集合
     * @param i                    页签行号
     */
    private void judgeAssembleDriverTime(String modelId, String currentDriverGroupId, XSSFSheet sheet, List<Driver> driverList, Driver driver, List<DriverItem> driverItemList, int i) throws ABCException {
        //情况1：当前遍历行是最后一行，表明 数据已经遍历到底，表明 当前动因已经遍历完成
        if (i == sheet.getPhysicalNumberOfRows() - 1) {
            assert driver != null : DRIVER_ERROR_NULL;
            assembleDriver(modelId, currentDriverGroupId, driverList, driver, driverItemList);

        } else {
            //获取 当前行 其下紧挨着的 由 null、“”组成的空行
            List<Integer> processEmptyRows = getProcessEmptyRows(sheet,i);

            if (!processEmptyRows.isEmpty()) {

                int bigestRowNum = processEmptyRows.get(processEmptyRows.size() - 1);

                //情况2：当前遍历行 下面还有很多行 这些行全部都是空行，此时类似于情况1
                if (bigestRowNum == sheet.getPhysicalNumberOfRows() - 1) {
                    assert driver != null : DRIVER_ERROR_NULL;
                    assembleDriver(modelId, currentDriverGroupId, driverList, driver, driverItemList);
                    return;
                }

                //情况3：当前遍历行是一个动因结束动因项 与 另一个动因开始动因项 之间有若干空行，此时上一动因遍历完成，需要保存
                int startDriverItemRow = bigestRowNum + 1;
                boolean isStart = isStart(sheet, startDriverItemRow);

                if (isStart) {
                    assert driver != null : DRIVER_ERROR_NULL;
                    assembleDriver(modelId, currentDriverGroupId, driverList, driver, driverItemList);
                }

            } else {
                //情况4：当前遍历行其下没有空行，是另一个动因开始动因项，此时 与 情况3类似
                boolean isStart = isStart(sheet, i + 1);

                if (isStart) {
                    assert driver != null : DRIVER_ERROR_NULL;
                    assembleDriver(modelId, currentDriverGroupId, driverList, driver, driverItemList);
                }

            }

        }
    }

    /**
     * 记录 全由 null、“” 构成的空行
     *
     * @param sheet     表对象
     * @param emptyRows 空行集合
     */
    private void processEmptyRows(XSSFSheet sheet, List<Integer> emptyRows) {
        for (int k = 0; k < sheet.getPhysicalNumberOfRows(); k++) {
            XSSFRow row = sheet.getRow(k);
            boolean isEmptyRow = true;
            for (int j = 0; j < row.getLastCellNum(); j++) {
                Cell cell = row.getCell(j);
                //一行中存在左侧单元格内容为 null 右侧单元格内容为 “” 的情况
                if (cell == null) {
                    continue;
                }
                DataFormatter formatter = new DataFormatter();
                String cellValue = formatter.formatCellValue(cell).trim();

                if (!"".equals(cellValue)) {
                    isEmptyRow = false;
                    break;
                }
            }

            if (isEmptyRow) {
                emptyRows.add(k);
            }
        }
    }


    /**
     * 获取 当前行 其下紧挨着的 由 null、“”组成的空行
     * @param sheet 表格对象
     * @param i 当前行
     * @return 处理结果
     */
    private List<Integer> getProcessEmptyRows(XSSFSheet sheet,int i) {
        List<Integer> emptyRows = new ArrayList<>();
        List<Integer> processEmptyRows = new ArrayList<>();

        //获取所有空行
        processEmptyRows(sheet, emptyRows);

        //（如果有）找到当前行其下第一个空行
        int index = emptyRows.indexOf(i + 1);
        if (index != -1) {
            //确保 “连续“
            for (int j = index; j < emptyRows.size(); j++) {
                Integer curNum = emptyRows.get(j);
                if (j == index){
                    processEmptyRows.add(curNum);
                }else {
                    if (curNum - emptyRows.get(j - 1) == 1) {
                        processEmptyRows.add(curNum);
                    }else {
                        break;
                    }
                }
            }
        }
        return processEmptyRows;
    }

    /**
     * 判断当前行 是否对应着 一个动因的初始动因项
     *
     * @param sheet 表对象
     * @param i     判断行号
     * @return 判断结果
     */
    private boolean isStart(XSSFSheet sheet, int i) {
        boolean isStart = true;
        XSSFRow row = sheet.getRow(i);
        Cell cell = row.getCell(0);

        //一行中存在左侧单元格内容为 null 右侧单元格内容为 “” 的情况
        if (cell == null) {
            isStart = false;
        } else {
            DataFormatter formatter = new DataFormatter();
            String cellValue = formatter.formatCellValue(cell).trim();
            if ("".equals(cellValue)) {
                isStart = false;
            }
        }
        return isStart;
    }

    /**
     * 组装动因对象
     *
     * @param modelId              模型ID
     * @param currentDriverGroupId 当前动因分组ID
     * @param driverList           动因集合
     * @param driver               预组装动因
     * @param driverItemList       动因值集合
     */
    private void assembleDriver(String modelId, String currentDriverGroupId, List<Driver> driverList, Driver driver, List<DriverItem> driverItemList) throws ABCException {
        driver.setBelongModelId(modelId);
        driver.setBelongDriverGroupId(currentDriverGroupId);

        List<String> dimNameList = new ArrayList<>();
        driverItemList.forEach(item -> dimNameList.add(item.getDimName()));
        //去重
        driver.setDimNameList(dimNameList.stream().distinct().collect(Collectors.toList()));

        //导入动因时，如果动因值编码为空，自动生成动因值编码
        //记录 动因值编码为空 的动因值
        HashMap<Integer,DriverItem> noneDriverItemCodeMap = new HashMap<>();
        for (int i = 0; i < driverItemList.size(); i++) {
            if (StringUtils.isEmpty(driverItemList.get(i).getCode())) {
                noneDriverItemCodeMap.put(i, driverItemList.get(i));
            }
        }
        //自动生成 动因值编码
        int noneCodeIndex = 0;
        for(Integer key:noneDriverItemCodeMap.keySet()){
            String driverItemIndexPlus = "";

            noneCodeIndex = noneCodeIndex + 1;
            if (noneCodeIndex < 9 || noneCodeIndex == 9){
                driverItemIndexPlus = "00"+noneCodeIndex;
            }else if (noneCodeIndex < 99 || noneCodeIndex == 99){
                driverItemIndexPlus = "0"+ noneCodeIndex;
            }else if (noneCodeIndex < 999 || noneCodeIndex == 999){
                driverItemIndexPlus = String.valueOf(noneCodeIndex);
            }

            DriverItem driverItem = driverItemList.get(key);
            driverItem.setCode(driver.getCode() + driverItemIndexPlus);
        }


        //导入动因时，如果动因值名称为空，自动生成动因值名称
        //记录 动因值名称为空 的动因值
        HashMap<Integer,DriverItem> noneDriverItemNameMap = new HashMap<>();
        for (int i = 0; i < driverItemList.size(); i++) {
            if (StringUtils.isEmpty(driverItemList.get(i).getName())) {
                noneDriverItemNameMap.put(i, driverItemList.get(i));
            }
        }
        //自动生成 动因值名称
        ABCHomeVo abcHomeVo = abcModelService.queryPeriodAndDW(modelId);
        List<String> periodBeanList = abcHomeVo.getPeriodBeanList();
        String period = periodBeanList.get(periodBeanList.size() - 1);
        for(Integer key:noneDriverItemNameMap.keySet()){
            DriverItem driverItem = driverItemList.get(key);
            String dimItemName;
            try {
                dimItemName = memberProvider.get(driverItem.getDimName(), driverItem.getMemberCode(), period).getName();
            } catch (ABCIntfException e) {
                String message = "导入失败，自动生成动因值名称时接口异常";
                logger.error(message);
                throw new ABCException(message);
            }
            driverItem.setName(dimItemName);
        }


        driver.setDriverItemList(driverItemList);
        driverList.add(driver);
    }

    /**
     *  动因导入最终处理
     * @param currentDriverGroupId  当前动因分组ID
     * @param driverList 动因集合
     * @param driverCheckVo 动因检查对象
     * @throws ABCException 异常
     */
    private void finalProcess(String currentDriverGroupId, List<Driver> driverList, DriverCheckVo driverCheckVo) throws ABCException {
        if (driverCheckVo.isCheckPass()) {
            driverService.batchSaveDriversAndDriverItems(currentDriverGroupId, driverList);
        }else {
            String exceptionInfo = String.format("导入失败，动因编码（%S）在动因分组（%S）中已经存在，请重新编辑",
                    driverCheckVo.getCheckDriverCode(),
                    driverCheckVo.getRepeatDriverGroupTitle());
            logger.error(exceptionInfo);
            throw new ABCException(exceptionInfo);
        }
    }

    //设置Excel表头
    private final static String[] cellText = {"动因编码", "动因名称", "关联维度编码", "关联维度名称", "关联维成员编码", "关联维成员名称", "动因值编码", "动因值名称", "取数公式"};

    /**
     * 根据前端 模型ID 和 动因分组ID 获取 动因集合 后 处理成excel文件 发送给前端 进行下载
     *
     * @param modelId       模型ID
     * @param driverGroupId 动因分组ID
     * @param response      返回响应
     */
    public void processExportData(String driverGroupId, String modelId, HttpServletResponse response) {
        //①根据前端参数获取 前端当前显示的内容
        List<Driver> driverList = getDriverListByDriverGroupId(driverGroupId, modelId);

        //②将数据组装成excel格式
        //创建行样式
        XSSFWorkbook workbook = new XSSFWorkbook();
        XSSFCellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setAlignment(HorizontalAlignment.LEFT);
        //获取设置的语言

        //创建sheet
        XSSFSheet sheet = createSheet(workbook, 0, cellText);
        sheet.setDefaultColumnWidth(25);
        /*设置行高*/
        sheet.setDefaultRowHeight((short) 400);

        //开始处理数据，创建表格
        setDriverData(driverList, sheet, cellStyle);


        //③数据组装成功，向前端发送
        ServletOutputStream stream = null;
        try {


            response.setHeader("Content-Disposition", "attachment; filename=myfile.xlsx");
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

            stream = response.getOutputStream();
            workbook.write(stream);
            stream.flush();

            stream.close();
            workbook.close();


        } catch (IOException e) {
            logger.error("动因数据导出异常", e);
        }
    }

    /**
     * 创建 sheet
     *
     * @param workbook Excel表
     * @param rownum   行号
     * @param text     文本
     * @return XSSFSheet 返回sheet
     */
    private XSSFSheet createSheet(XSSFWorkbook workbook, int rownum, String[] text) {
        XSSFCellStyle headstyle = workbook.createCellStyle();
        headstyle.setAlignment(HorizontalAlignment.LEFT);

        XSSFSheet sheet = workbook.createSheet(String.format("第%s页", rownum + 1));
        XSSFRow title = sheet.createRow(0);

        for (int i = 0; i < text.length; i++) {
            createCell(title, headstyle, i, text[i]);
        }
        return sheet;
    }

    /**
     * 创建行并填充记录
     *
     * @param sheet     表格sheet
     * @param cellStyle 单元格样式
     */
    private void createRow(XSSFSheet sheet, XSSFCellStyle cellStyle, DriverInfoVo driverInfoVo) {
        XSSFRow row = sheet.createRow(sheet.getLastRowNum() + 1);
        for (int i = 0; i < 9; i++) {
            switch (i) {
                case 0:
                    createCell(row, cellStyle, i, driverInfoVo.getDriverCode());
                    break;
                case 1:
                    createCell(row, cellStyle, i, driverInfoVo.getDriverName());
                    break;
                case 2:
                    createCell(row, cellStyle, i, driverInfoVo.getDimCode());
                    break;
                case 3:
                    createCell(row, cellStyle, i, driverInfoVo.getDimName());
                    break;
                case 4:
                    createCell(row, cellStyle, i, driverInfoVo.getDimItemCode());
                    break;
                case 5:
                    createCell(row, cellStyle, i, driverInfoVo.getDimItemName());
                    break;
                case 6:
                    createCell(row, cellStyle, i, driverInfoVo.getDriverItemCode());
                    break;
                case 7:
                    createCell(row, cellStyle, i, driverInfoVo.getDriverItemName());
                    break;
                case 8:
                    createCell(row, cellStyle, i, driverInfoVo.getFormula());
                    break;
                default:
                    logger.error("异常值");
            }

        }
    }

    /**
     * 创建单元格
     *
     * @param row   行
     * @param style 样式
     * @param index 索引
     * @param text  文本
     */
    private void createCell(XSSFRow row, XSSFCellStyle style, int index, String text) {
        XSSFCell cell = row.createCell(index);
        cell.setCellValue(text);
        cell.setCellStyle(style);
    }


    /**
     * 为单元格赋值
     *
     * @param driverList 动因信息
     * @param sheet      表格一个页签
     * @param cellStyle  样式
     */
    private void setDriverData(List<Driver> driverList, XSSFSheet sheet, XSSFCellStyle cellStyle) {
        for (Driver driver : driverList) {
            List<DriverItem> driverItemList = driver.getDriverItemList();
            for (int i = 0; i < driverItemList.size(); i++) {
                //组建行数据
                DriverItem driverItem = driverItemList.get(i);
                DriverInfoVo driverInfoVo = new DriverInfoVo();
                if (i == 0) {
                    //每个动因的第一行 设置 动因编码、动因名称
                    driverInfoVo.setDriverCode(driver.getCode());
                    driverInfoVo.setDriverName(driver.getName());
                }
                driverInfoVo.setDimCode(driverItem.getDimName());
                driverInfoVo.setDimName(driverItem.getDimTitle());
                driverInfoVo.setDimItemCode(driverItem.getMemberCode());
                driverInfoVo.setDimItemName(driverItem.getDimItemName());
                driverInfoVo.setDriverItemCode(driverItem.getCode());
                driverInfoVo.setDriverItemName(driverItem.getName());
                driverInfoVo.setFormula(driverItem.getDriverItemFormula().getFormula());

                //创建行
                createRow(sheet, cellStyle, driverInfoVo);
            }
        }
    }


    /**
     * 检测 当前模型 已经入库的 动因集合中 是否存在和待检查动因集合 相同的动因编码 存在
     *
     * @param modelId            当前模型ID
     * @param preCheckdriverList 待检查动因集合
     * @return 检查结果
     */
    public boolean driverCodeCheckViaExistedDrivers(String modelId, List<Driver> preCheckdriverList) {
        //当前模型所有的 动因集合
        List<Driver> allDriversFromCurrentModel = abcDriverDao.getDriverListByModelId(modelId);

        for (Driver driver : allDriversFromCurrentModel) {
            for (Driver preCheckDriver : preCheckdriverList) {
                if (preCheckDriver.getCode().equals(driver.getCode())) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 检测 当前模型 除当前分组外的 动因集合中 是否存在和待检查动因集合 相同的动因编码 存在
     *
     * @param modelId              当前模型ID
     * @param currentDriverGroupId 当前动因分组ID
     * @param preCheckdriverList   待检查动因集合
     * @return 检查结果
     */
    public DriverCheckVo driverCodeCheckViaExistedDriversExcCurrGroup(String modelId, String currentDriverGroupId, List<Driver> preCheckdriverList) {
        //当前模型所有的 动因集合
        List<Driver> allDriversFromCurrentModel = abcDriverDao.getDriverListByModelId(modelId);

        //获取 除当前分组外的 动因集合
        List<Driver> filtedDriverList = new ArrayList<>();
        allDriversFromCurrentModel.stream()
                .filter(preFilterItem -> !preFilterItem.getBelongDriverGroupId().equals(currentDriverGroupId))
                .forEach(filtedDriverList::add);

        for (Driver driver : filtedDriverList) {
            for (Driver preCheckDriver : preCheckdriverList) {
                if (preCheckDriver.getCode().equals(driver.getCode())) {
                    DriverGroup byDriverGroupId = abcDriverGroupService.getDriverGroupByDriverGroupId(driver.getBelongDriverGroupId());
                    String repeatDriverGroupTitle = byDriverGroupId.getTitle();
                    return new DriverCheckVo(false, driver.getCode(), repeatDriverGroupTitle);
                }
            }
        }
        return new DriverCheckVo(true, null, null);
    }

    /**
     * 自动生成 指定模型ID 对应模型下 不重复的 动因编码
     *
     * @param modelId              指定模型ID
     * @param curPageAllDriverList 当前页面所有动因集合，包含已保存动因和未保存动因
     * @return 动因编码
     */
    public String autoGenDriverCode(String modelId, List<Driver> curPageAllDriverList) {
        List<Driver> driverList = getDriverListByModelId(modelId);

        for (Driver preFilterItem : curPageAllDriverList) {
            if (!driverList.stream()
                    .map(Driver::getCode)
                    .collect(Collectors.toList())
                    .contains(preFilterItem.getCode())) {
                driverList.add(preFilterItem);
            }
        }

        int preNumPart = 1;
        String preSetName = String.format("DRIVER_%d", preNumPart);

        //检测当前名称是否存在
        List<String> existedTitleList = driverList.stream().map(Driver::getCode).collect(Collectors.toList());
        boolean flag = true;
        do {
            if (!existedTitleList.contains(preSetName)) {
                flag = false;
            } else {
                preNumPart++;
                preSetName = String.format("DRIVER_%d", preNumPart);
            }
        } while (flag);

        return preSetName;
    }

    /**
     * 根据  动因分组ID集合  批量删除 多个动因
     *
     * @param driverGroupIdList 多个动因分组id集合
     */
    @Transactional(rollbackFor = Exception.class)
    public void batchDeleteDriversByDriverGroupIdList(List<String> driverGroupIdList) {
        //获取当前模型ID
        String driverGroupId = "";
        String modelId = "";
        if (!driverGroupIdList.isEmpty()) {
            driverGroupId = driverGroupIdList.get(0);
        }
        if (!"".equals(driverGroupId)) {
            modelId = abcDriverGroupService.getDriverGroupByDriverGroupId(driverGroupId).getBelongModelId();
        }


        List<String> driverIdList = new ArrayList<>();
        List<Driver> driverList = abcDriverDao.getDriverListByDriverGroupIds(driverGroupIdList);
        driverList.stream()
                .map(Driver::getId)
                .forEach(driverIdList::add);
        abcDriverItemService.batchDeleteDriverItemsByDriverIds(driverIdList);
        abcDriverDao.batchDeleteDriversByDriverGroupIdList(driverGroupIdList);

        //更新abc模型的修改时间
        if (!"".equals(modelId)) {
            abcBeanService.updateModifyTime(modelId);
        }

    }
}
